﻿using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;

namespace JESAI.Framework.Idempotents
{
    /// <summary>
    /// api request idempotence
    /// </summary>
    public class HttpIdempotentAttribute : Attribute, IAsyncResourceFilter
    {

        static HttpIdempotentAttribute()
        {
            var thread = new Thread(() =>
            {
                while (true)
                {
                    var hashtableDatas = HashTable.Where(x => DateTime.Now > x.Value.Time).ToList();

                    if (hashtableDatas.Any())
                    {
                        foreach (var hashtableData in hashtableDatas)
                        {
                            HashTable.Remove(hashtableData.Key);
                        }
                    }
                    else
                    {
                        System.Threading.Thread.Sleep(2000);
                    }
                }

            });

            thread.IsBackground = true;
            thread.Start();
        }

        /// <summary>
        /// http request parameter code collect
        /// </summary>
        private static readonly Dictionary<int, HashtableData> HashTable = new();

        /// <summary>
        /// http request parameter code
        /// </summary>
        public int _httpHasCode;

        /// <summary>
        /// waiting for the last request , default value:1000
        /// </summary>
        public double WaitMillisecond { get; set; } = 1000;

        /// <summary>
        /// result cache Millisecond , default value:1000
        /// </summary>
        public double CacheMillisecond { get; set; } = 1000;

        /// <summary>
        /// interceptor
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
        {
            await this.SetHttpParameterHasCode(context.HttpContext);

            if (HashTable.ContainsKey(_httpHasCode))
            {
                var hashtableData = (HashtableData)HashTable[_httpHasCode];
                if (hashtableData != null)
                {
                    if (DateTime.Now < hashtableData.Time)
                    {
                        context.Result = hashtableData.Value;
                        return;
                    }
                    else if (DateTime.Now > hashtableData.Time)
                    {
                        HashTable.Remove(_httpHasCode);
                    }
                    else if (hashtableData.Value.Value == null)
                    {
                        context.Result = new ContentResult()
                        {
                            Content = "正在执行..."
                        };
                        return;
                    }
                }
            }

            HashTable.Add(_httpHasCode, new HashtableData(DateTime.Now.AddMilliseconds(WaitMillisecond), null));

            try
            {
                var netResult = await next();

                if (HashTable.ContainsKey(_httpHasCode))
                {
                    var hashtableData = (HashtableData)HashTable[_httpHasCode];
                    if (hashtableData != null)
                    {
                        hashtableData.Value = (ObjectResult)netResult.Result;
                        hashtableData.Time = DateTime.Now.AddMilliseconds(CacheMillisecond);
                    }
                }
            }
            catch (Exception ex)
            {
                HashTable.Remove(_httpHasCode);
            }

        }

        /// <summary>
        /// get http request  parameter code
        /// </summary>
        /// <returns></returns>
        private async Task SetHttpParameterHasCode(HttpContext httpContext)
        {

            object readFromJson = null;

            try
            {
                if (httpContext.Request.Method != "GET")
                {
                    readFromJson = await httpContext.Request.ReadFromJsonAsync<object>();

                    //httpContext.Request.EnableBuffering();//设置流可以多次读取
                    //readFromJson = await new StreamReader(httpContext.Request.Body).ReadToEndAsync();
                    //httpContext.Request.Body.Seek(0, SeekOrigin.Begin);//设置流的栈位置回到起点
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            //todo 根据实际项目情况处理，获取Headers toke
            var authorization = httpContext.Request.Headers["Authorization"];

            var queryString = httpContext.Request.QueryString;
            var bodyString = readFromJson == null ? string.Empty : readFromJson.ToString();

            var builder = $"{authorization}-{queryString}-{bodyString}";

            this._httpHasCode = builder.GetHashCode();
        }

        /// <summary>
        /// Hashtable parameter model
        /// </summary>
        private class HashtableData
        {
            public HashtableData(DateTime time, ObjectResult value)
            {
                Time = time;
                Value = value;
            }
            public DateTime Time { get; set; }
            public ObjectResult Value { get; set; }
        }
    }
}
